/**********************************************************************************************************************
 * @file    sh_task.c
 * @author  Queclink Hubert.Lee
 * @date    2018-05-18
 * @brief   Shell task command.
 *
 * Copyright (C) 2018 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 *
 * @attention
 *
 *********************************************************************************************************************/

/* includes ---------------------------------------------------------------------------------------------------------*/

#include <stdio.h>
#include "lettershell/shell.h"
#include "cmsis_os2.h"
#include "FreeRTOS.h"
#include "task.h"

#include "bsp_uart.h"

#include "global.h"
#include <qlcommon.h>

typedef struct {
    char *head;
    size_t len;
} task_msg_node;

/*
 * @brief  打印信息按首字母的大小重新组合.
 * @param  printf_msg: [I], 打印的信息.
 * @retval none.
 */
static void printf_msg_recombine(char *printf_msg)
{
    size_t printf_len = strlen(printf_msg);

    char *new_msg = NULL;
    if (NULL == (new_msg = qlmalloc(printf_len + 1)))
        return;

    memset(new_msg, 0, printf_len + 1);

    int cnt                      = uxTaskGetNumberOfTasks();
    task_msg_node *task_msg_list = NULL;
    if (NULL == (task_msg_list = qlmalloc(sizeof(task_msg_node) * cnt))) {
        qlfree(new_msg);

        return;
    }

    memset(task_msg_list, 0, sizeof(task_msg_node) * cnt);

    int valid_cnt = 0;
    char *p       = printf_msg;
    for (; valid_cnt < cnt && 10 < printf_len; valid_cnt++) {
        char *end = quec_memstr(p, printf_len, "\r\n");
        if (NULL == end)
            break;

        task_msg_list[valid_cnt].head = p;
        task_msg_list[valid_cnt].len  = end - p + 2;
        p += task_msg_list[valid_cnt].len;
        printf_len -= task_msg_list[valid_cnt].len;
    }

    for (int i = 0; i < valid_cnt - 1; i++) { /* 冒泡法 */
        for (int j = 1; j < valid_cnt - i; j++) {
            if (0 < strcmp(task_msg_list[j - 1].head, task_msg_list[j].head)) {
                task_msg_node swap;
                swap.head                 = task_msg_list[j - 1].head;
                swap.len                  = task_msg_list[j - 1].len;
                task_msg_list[j - 1].head = task_msg_list[j].head;
                task_msg_list[j - 1].len  = task_msg_list[j].len;
                task_msg_list[j].head     = swap.head;
                task_msg_list[j].len      = swap.len;
            }
        }
    }

    p = new_msg;
    for (int i = 0; i < valid_cnt; i++) {
        memcpy(p, task_msg_list[i].head, task_msg_list[i].len);
        p += task_msg_list[i].len;
    }

    memcpy(printf_msg, new_msg, strlen(new_msg));

    qlfree(new_msg);
    qlfree(task_msg_list);
}


extern TickType_t xTicksToJumpTotal;
extern void vLastTaskTableGet(char *pcWriteBuffer);
static int do_task(int argc, char *const argv[]) /* 有修改操作,请同时更新task_wdg.c. */
{
    int real_len           = 0;
    void (*getmsg)(char *) = (1 == argc) ? vTaskGetRunTimeStats : vTaskList;
    char *msg;
    char *p;

    if ((1 == argc) ||
        ((2 == argc) && (!strcasecmp((const char *)argv[1], "s")))) { /* "task" or "task -s" */
        int len = uxTaskGetNumberOfTasks() * 70;
        if (len < 160) /* vLastTaskTableGet()函数需要160bytes. */
            len = 160;

        if (NULL == (msg = qlmalloc(len)))
            return -EIO;

        memset(msg, 0, len);
        getmsg(msg);
        printf_msg_recombine(msg);

        UBaseType_t task_priority;
        task_priority = uxTaskPriorityGet(NULL);
        vTaskPrioritySet(NULL, configMAX_PRIORITIES - 1);
        vLastTaskTableEnable(0);
        bsp_uart_waitfor_transmit_finished(debug_uart_id);
        if (1 == argc) {
            printf("Name          Counter(s)  Usage  RunCnt    RunMax(ms)  BlockMax(ms)\r\n");
            printf("===================================================================\r\n");
        } else {
            printf("Name          State  Priority  Stack   Num    Canstop\r\n");
            printf("=====================================================\r\n");
        }

        p = msg;
        while (1) {
            real_len = printf("%s", p);
            if (0 > real_len)
                break;

            p += real_len;
            if (0 == strlen(p))
                break;
        }
        printf("\r\n");

        if (1 == argc) {
            bsp_uart_waitfor_transmit_finished(debug_uart_id);
            if (0 < xTicksToJumpTotal) {
                unsigned int seconds = (xTicksToJumpTotal * portTICK_PERIOD_MS) / 1000;
                printf("The CPU has slept total %u seconds (%u percent) since the system boot.\r\n",
                       seconds, seconds * 100 / getsec());
            }
            memset(msg, 0, 160);
            vLastTaskTableGet(msg);
            printf("%s", msg);
            printf("\r\n");
        }
        vTaskPrioritySet(NULL, task_priority);
        vLastTaskTableEnable(1);
        qlfree(msg);
        return 0;
    } else {
        return -EINVAL;
    }
}

/*
 * 使用 Shell 打印 task 信息, 慎用.
 */
void vTaskGetRunTimeStatsPrintf(void)
{
    char *argv[1] = {"task"};

    do_task(1, argv);
}

/*
 * 使用 Shell 打印 task -s 信息, 慎用.
 */
void vTaskListPrintf(void)
{
    char *argv[2] = {"task", "s"};

    do_task(2, argv);
}


SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_DISABLE_RETURN,
                 task, do_task, show the task info);